﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Transactions;
using DomainDriver.DomainModeling.DomainModels;
using DomainDriver.DomainModeling.Exceptions;

namespace DomainDriver.DomainModeling.TransactionScripts
{
    public abstract class TransactionScriptBase : ITransactionScript
    {
        private TransactionScopeOption m_TransactionScopeOption;
        private IDomainModel m_DomainModel;
        private bool m_HasRun;
        private Nullable<bool> m_WasSuccessful;

        public TransactionScriptBase()
        {
            m_TransactionScopeOption = TransactionScopeOption.Required;
            m_DomainModel = null;
            m_HasRun = false;
            m_WasSuccessful = null;
        }

        public TransactionScopeOption TransactionScopeOption
        {
            get { return m_TransactionScopeOption; }
        }

        public IDomainModel DomainModel
        {
            get { return m_DomainModel; }
            set
            {
                if (m_DomainModel != null)
                { throw new ApplicationException("DomainModel can only be set once."); }
                m_DomainModel = value;
            }
        }

        public virtual bool IsReady()
        {
            return false;
        }

        public bool HasRun
        {
            get { return m_HasRun; }
        }

        public bool WasSuccessful
        {
            get
            {
                if (!HasRun)
                { throw new TransactionScriptFailureException("The TransactionScript has not run yet."); }
                return m_WasSuccessful.Value;
            }
        }

        public void Run()
        {
            if (HasRun)
            { throw new TransactionScriptFailureException("The TransactionScript has already run."); }
            if (!IsReady())
            { throw new TransactionScriptFailureException("The TransactionScript is not ready to be run."); }

            m_WasSuccessful = false;
            m_HasRun = true;

            TransactionScope transactionScope = null;
            try
            {
                transactionScope = new TransactionScope(TransactionScopeOption);

                RunScriptLogic();

                transactionScope.Complete();
                m_WasSuccessful = true;
            }
            catch (Exception exception)
            {
                throw new TransactionScriptFailureException("TransactionScript failed to complete successfully.", exception);
            }
            finally
            {
                if (transactionScope != null)
                { transactionScope.Dispose(); }
            }
        }

        protected abstract void RunScriptLogic();
    }

    public abstract class TransactionScriptBase<RETURN_TYPE> : TransactionScriptBase, ITransactionScript<RETURN_TYPE>
    {
        private RETURN_TYPE m_Result;

        public TransactionScriptBase()
        {
            m_Result = default(RETURN_TYPE);
        }

        public RETURN_TYPE Result
        {
            get
            {
                if (!HasRun)
                { throw new TransactionScriptFailureException("The TransactionScript has not run yet."); }
                if (!WasSuccessful)
                { throw new TransactionScriptFailureException("The TransactionScript did not complete successfully."); }
                return m_Result;
            }
        }

        protected override void RunScriptLogic()
        {
            m_Result = RunScriptLogicWithResult();
        }

        protected abstract RETURN_TYPE RunScriptLogicWithResult();
    }
}